///////////////////////////////////////////////////////////////////////////////
//Copyright 2009 Christopher Betancourt
//
//This file is part of PEAQ
//
//PEAQ is free software: you can redistribute it and/or modify it under the 
//terms of the GNU Lesser General Public License as published by the Free 
//Software Foundation, either version 3 of the License, or (at your option) any
//later version.
//
//PEAQ is distributed in the hope that it will be useful, but WITHOUT ANY 
//WARRANTY; without even the implied warranty of MERCHANTABLILTY or FITTNESS FOR
//A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more 
//details.
//
//You should have received a copy of the GNU Lesser Public License with PEAQ.  
//If not, see <http://www.gnu.org/licenses/>.
////////////////////////////////////////////////////////////////////////////////
package extractColor;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;

import processing.*;

public class ExtractColorModule implements ImageProcessor {

//	private float[] highComponenets;

	public int[][] extractPattern(BufferedImage image, ProcessingParams params)
	{
		ExtractColorParams ecParams=(ExtractColorParams)params;
		int pixWidth=ecParams.getPixWidth();
		int pixHeight=ecParams.getPixHeight();
		Color high=ecParams.getHigh();
		Color low=ecParams.getLow();
		int rgbComponents[]=new int[3];
		float hsbComponents[]=new float[3];
		float highComponents[]=new float[3];
		float lowComponents[]=new float[3];
		ColorModel cm=image.getColorModel();
		int retVal[][]=new int[image.getHeight()/pixHeight+1][image.getWidth()/pixWidth+1];
		int xNew=-1; //-1 to compensate for double count of 0
		int yNew=-1; //-1 to compensate for double count of 0
		
		//figure out the HSV components of the parameters.
		Color.RGBtoHSB(high.getRed(), high.getGreen(), high.getBlue(), 
				highComponents);
		Color.RGBtoHSB(low.getRed(), low.getGreen(), low.getBlue(),
				lowComponents);
		//swap around components so that lowComponents[0]<=highComponents[0] and
		//so on.  This is to simplify the tests of whether a given color's 
		//components lie between those of high and low later on.  The hue 
		//components are arranged so as to minimize the angular distance.
		if(highComponents[0]<lowComponents[0])
		{
			float temp=highComponents[0];
			highComponents[0]=lowComponents[0];
			lowComponents[0]=temp;
		}
		//check to see if this arrangement is the minimal angular distance.
		if(highComponents[0]-lowComponents[0]>Math.abs(highComponents[0]-(1.0+lowComponents[0])))
		{
			float temp=highComponents[0];
			highComponents[0]=(float)1.0+lowComponents[0];
			lowComponents[0]=temp;
		}
		if(highComponents[1]<lowComponents[1])
		{
			float temp=highComponents[1];
			highComponents[1]=lowComponents[1];
			lowComponents[1]=temp;
		}
		if(highComponents[2]<lowComponents[2])
		{
			float temp=highComponents[2];
			highComponents[2]=lowComponents[2];
			lowComponents[2]=temp;
		}
		
		//initialize retVal
		for(int y=0;y<retVal.length;++y)
		{
			for(int x=0;x<retVal[y].length;++x)
			{
				retVal[y][x]=0;
			}
		}
		
		//now find the colored regions.
		for(int y=0;y<image.getHeight();++y)
		{
			if(y%pixHeight==0)
				++yNew;
			for(int x=0;x<image.getWidth();++x)
			{
				if(x%pixWidth==0)
					++xNew;
				
				//get the color components and check to see if they are in range
				cm.getComponents(image.getRGB(x, y), rgbComponents, 0);
				Color.RGBtoHSB(rgbComponents[0], rgbComponents[1], 
						rgbComponents[2], hsbComponents);
				if(hsbComponents[0]>=lowComponents[0] && 
						hsbComponents[0]<=highComponents[0])
				{
					//the hue is in range, now check the saturation
					if(hsbComponents[1]>=lowComponents[1] &&
							hsbComponents[1]<=highComponents[1])
					{
						//saturation is in range, check the value
						if(hsbComponents[2]>=lowComponents[2] &&
								hsbComponents[2]<=highComponents[2])
						{
							++retVal[yNew][xNew];
						}//end value if
					}//end saturation if
				}//end hue if
			}//end x for
			xNew=-1; //reset xNew for the next row.
		}//end y for
		
		//now that we have the hit pixel count, see if we have a majority.  Ties
		//favor hit.
		for(int y=0; y<retVal.length;++y)
		{
			for(int x=0;x<retVal[y].length;++x)
			{
				if(retVal[y][x]>=(pixWidth*pixHeight)/2.0)
				{
					retVal[y][x]=1;
				}
				else
				{
					retVal[y][x]=0;
				}
			}//end x for
		}//end y for
		
		return retVal;
	}
	
	public BufferedImage renderPattern(int [][] pattern, ProcessingParams params)
	{
		ExtractColorParams ecParams=(ExtractColorParams)params;
		int pixWidth=ecParams.getPixWidth();
		int pixHeight=ecParams.getPixHeight();
		Color target=ecParams.getTarget();
		float hsbComponents[]=new float[3];
		BufferedImage retVal=new BufferedImage(pattern[0].length*pixWidth,
				pattern.length*pixHeight,BufferedImage.TYPE_INT_RGB);
		Graphics2D g2d=(Graphics2D)retVal.getGraphics();
		
		//get the value channel of the target color so that we know if we need
		//a dark or a light background.
		Color.RGBtoHSB(target.getRed(), target.getGreen(), target.getBlue(),
				hsbComponents);
		if(hsbComponents[2]<0.5)
		{
			//dark color so a light background
			g2d.setBackground(Color.white);
		}
		else
		{
			//light color so dark background
			g2d.setBackground(Color.black);
		}
		g2d.clearRect(0, 0, retVal.getWidth(), retVal.getHeight());
		
		//step through the data, and draw the hits using the target color.
		g2d.setPaint(target);
		for(int y=0; y<pattern.length;++y)
		{
			for(int x=0;x<pattern[y].length;++x)
			{
				if(pattern[y][x]==1)
				{
					g2d.fill(new Rectangle2D.Double(x*pixWidth,y*pixHeight,
							pixWidth,pixHeight));
				}
				else if(pattern[y][x]!=0)
				{
					//this is an error, we don't know how to render it.
					throw(new IllegalArgumentException(
							"Unknown pattern designator "+
							((Integer)pattern[y][x]).toString()));
				}
			}//end x for
		}//end y for
		return retVal;		
	}
}
